#include <device.h>
#include <pmemory.h>

#define NR_MAP 32
#define IO_SPACE_MAX (2 * 1024 * 1024)

static IOMap maps[NR_MAP] = {};
static int map_num = 0;
static uint8_t io_space[IO_SPACE_MAX];
static uint8_t *p_space = io_space;

void add_mmio_map(const char *name, uint64_t base, void *space, size_t size, io_callback_t callback) {
  uint64_t high = base + size - 1;
  for(size_t i = 0; i < map_num; i++) {
    if(!(maps[i].low > high || maps[i].high < base)) {
      printf("MMIO resion [0x%lx, 0x%lx] is overlapped\n", base, high);
      assert(0);
    }
  }
  if(map_num < NR_MAP){
    maps[map_num] = {.name = name, .low = base, .high = base + size - 1, .space = space, .callback = callback};
    map_num++;
  }
  else {
    printf("MMIO: too many devices\n");
    assert(0);
  }
}

uint8_t* new_space(int size) {
  uint8_t *p = p_space;
  // page aligned;
  size = (size + (PAGE_SIZE - 1)) & ~PAGE_MASK;
  p_space += size;
  assert((uint64_t)p_space - (uint64_t)io_space < IO_SPACE_MAX);
  return p;
}

void difftest_skip_ref();

uint64_t mmio_read(uint64_t addr, size_t len) {
  assert(len >= 1 && len <= 8);
  difftest_skip_ref();
  for(size_t i = 0; i < map_num; i++) {
    if(addr <= maps[i].high && addr >= maps[i].low) {
      uint32_t offset = addr - maps[i].low;
      if(maps[i].callback != NULL) {
        maps[i].callback(offset, len, false);
      }
      return host_read((char *)maps[i].space + offset, len);
    }
  }
  printf("MMIO: 0x%lx out of range\n", addr);
  fflush(stdout);
  assert(0);
  return 0;
}

void mmio_write(uint64_t addr, size_t len, uint64_t data) {
  assert(len >= 1 && len <= 8);
  difftest_skip_ref();
  for(size_t i = 0; i < map_num; i++) {
    if(addr <= maps[i].high && addr >= maps[i].low) {
      uint32_t offset = addr - maps[i].low;
      host_write((char *)maps[i].space + offset, len, data);
      if(maps[i].callback != NULL) {
        maps[i].callback(offset, len, true);
      }
      return;
    }
  }
  printf("MMIO: 0x%lx out of range\n", addr);
  fflush(stdout);
  assert(0);
  return;
}
